package com.jhs.demo.handpsd;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;


import com.jhs.demo.utils.MyUtils;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by wangqi on 2018/8/2.
 */
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class HandPsdView extends FrameLayout implements View.OnTouchListener {

    private static final int COLOR_GRAY_IN = Color.parseColor("#929292");
    private static final int COLOR_GRAY_OUT = Color.parseColor("#d7d7d7");
    private static final int COLOR_BULE_IN = Color.parseColor("#0294f5");
    private static final int COLOR_BULE_OUT = Color.parseColor("#8ab8d3");
    private static final int COLOR_RED_IN = Color.parseColor("#e90643");
    private static final int COLOR_RED_OUT = Color.parseColor("#8e0e30");

    private static final String NOTICE_SET_PASSWORD = "请设置密码";
    //    private static final String NOTICE_SET_PASSWORD_OLD = "请输入原始密码";
    private static final String NOTICE_SET_PASSWORD_AGAIN = "请再次设置密码";
    private static final String NOTICE_SET_PASSWORD_ERROR = "两次输入的密码不一致，请重新输入";
    private static final String NOTICE_SET_PASSWORD_SUCCESS = "恭喜！密码设置成功";

    // 这里只是临时存储手势密码
    private String tempPassword;

    private Paint mRoundPaint;
    private Paint mTrailPaint;

    private Lock lock;

    //每个圆圈之间的距离
    private float mPadding;

    //圆圈半径
    private float mRadius;

    //圆圈弧宽度
    private float rWidth = MyUtils.dp2px(2);

    //圆心的坐标集合
    private List<RoundPoint> points;

    //已选中的点
    private LinkedHashMap<Integer, RoundPoint> selectPoints = new LinkedHashMap<>();

    //最后一个点的位置
    private int lastPosition;

    //状态
    private HandState state = HandState.NORMAL;
    private float moveX;
    private float moveY;

    private boolean isTrailing;

    private HandViewListener listener;
    private TextPaint mTextPaint;

    private SettingState settingState = SettingState.SETTING;

    //滑动状态
    enum HandState {
        NORMAL, HANDING, ERROR
    }

    //设置状态
    enum SettingState {
        SETTING(NOTICE_SET_PASSWORD),
        SETTING_AGAIN(NOTICE_SET_PASSWORD_AGAIN),
        SETTING_ERROR(NOTICE_SET_PASSWORD_ERROR),
        SETTING_SUCCESS(NOTICE_SET_PASSWORD_SUCCESS);

        public String notice;

        SettingState(String notice) {
            this.notice = notice;
        }
    }

    public interface HandViewListener {
        void onComplete(String password);
        void onSettingSuccess();
    }

    public HandPsdView(@NonNull Context context) {
        this(context, null);
    }

    public HandPsdView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HandPsdView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setBackgroundColor(Color.WHITE);
        init();
    }

    private void init() {
        lock = new ReentrantLock();

        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(COLOR_RED_IN);
        mTextPaint.setTextSize(MyUtils.sp2px(18));

        mRoundPaint = new Paint();
        mRoundPaint.setAntiAlias(true);
        mRoundPaint.setDither(true);
        mRoundPaint.setStyle(Paint.Style.STROKE);

        mTrailPaint = new Paint();
        mTrailPaint.setStrokeWidth(20.0f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //默认初始值
        if (getHeight() > getWidth()) {
            mRadius = (getWidth() - 6 * rWidth) / 12;
        } else {
            mRadius = (getHeight() - 6 * rWidth) / 12;
        }
        mPadding = mRadius * 3 / 2;

        if (points == null) {
            points = new ArrayList<>();
            //找到每个圆的坐标
            points.add(new RoundPoint(getWidth() / 2 - mRadius * 2 - mPadding - rWidth * 2, getHeight() / 2 - mRadius * 2 - mPadding - rWidth * 2));
            points.add(new RoundPoint(getWidth() / 2, getHeight() / 2 - mRadius * 2 - mPadding - rWidth * 2));
            points.add(new RoundPoint(getWidth() / 2 + mRadius * 2 + mPadding + rWidth * 2, getHeight() / 2 - mRadius * 2 - mPadding - rWidth * 2));

            points.add(new RoundPoint(getWidth() / 2 - mRadius * 2 - mPadding - rWidth * 2, getHeight() / 2));
            points.add(new RoundPoint(getWidth() / 2, getHeight() / 2));
            points.add(new RoundPoint(getWidth() / 2 + mRadius * 2 + mPadding + rWidth * 2, getHeight() / 2));

            points.add(new RoundPoint(getWidth() / 2 - mRadius * 2 - mPadding - rWidth * 2, getHeight() / 2 + mRadius * 2 + mPadding + rWidth * 2));
            points.add(new RoundPoint(getWidth() / 2, getHeight() / 2 + mRadius * 2 + mPadding + rWidth * 2));
            points.add(new RoundPoint(getWidth() / 2 + mRadius * 2 + mPadding + rWidth * 2, getHeight() / 2 + mRadius * 2 + mPadding + rWidth * 2));
        }
        //绘制文字提示
        canvas.translate(0, getHeight() / 2 - mRadius * 3 - rWidth * 3 - mPadding * 2);
        StaticLayout titleLayout = new StaticLayout(settingState.notice, mTextPaint, getWidth(), Layout.Alignment.ALIGN_CENTER, 1f, 1f, true);
        titleLayout.draw(canvas);

        //回到原点
        canvas.translate(0, -(getHeight() / 2 - mRadius * 3 - rWidth * 3 - mPadding * 2));
        //绘制圆弧
        drawRound(canvas);
        //绘制轨迹
        if (state != HandState.NORMAL) {
            drawTrail(canvas);
        }
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                int downPosition = getPositionByXY(ev.getX(), ev.getY());
                if (downPosition != -1) {
                    state = HandState.HANDING;
                    //存入
                    selectPoints.put(downPosition, points.get(downPosition));
                    lastPosition = downPosition;
                    //刷新View
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //加这层判断 为了避免重复滑动
                if (state == HandState.HANDING) {
                    moveX = ev.getX();
                    moveY = ev.getY();
                    int movePosition = getPositionByXY(ev.getX(), ev.getY());
                    if (movePosition != -1) {
                        if (selectPoints.containsKey(movePosition)) {
                            isTrailing = false;
                        } else {
                            //判断两点之间是否经过中心
                            int centerPoint = isHaveCenterPoint(lastPosition, movePosition);
                            if (centerPoint != -1) {
                                selectPoints.put(centerPoint, points.get(centerPoint));
                            }
                            selectPoints.put(movePosition, points.get(movePosition));
                            lastPosition = movePosition;
                        }
                    } else {
                        isTrailing = true;
                    }
                    invalidate();
                    break;
                }
            case MotionEvent.ACTION_UP:
                if (state == HandState.HANDING) {
                    //拦截触摸事件、
                    setOnTouchListener(this);
                    isTrailing = false;
                    //获取设置完的密码
                    StringBuilder sb = new StringBuilder();
                    for (Integer i : selectPoints.keySet()) {
                        sb.append(i + 1);
                    }
                    String password = sb.toString();
                    if (listener != null) {
                        listener.onComplete(password);
                    }

                    //存入临时数据中
                    if (TextUtils.isEmpty(tempPassword)) {
                        settingState = SettingState.SETTING_AGAIN;
                        tempPassword = password;
                        delayedDeal(500);
                    } else {
                        //检验密码是否与第一次相同
                        if (tempPassword.equals(password)) {
                            settingState = SettingState.SETTING_SUCCESS;
                        } else {
                            settingState = SettingState.SETTING_ERROR;
                            state= HandState.ERROR;
                        }
                        tempPassword = "";
                        delayedDeal(1500);
                    }
                    invalidate();
                }
                break;
        }
        return true;
    }

    private void delayedDeal(long delayMillis) {
        postDelayed(new Runnable() {
            @Override
            public void run() {
                if (settingState == SettingState.SETTING_ERROR) {
                    settingState = SettingState.SETTING;
                } else if (settingState == SettingState.SETTING_SUCCESS) {
                    if (listener!=null)
                        listener.onSettingSuccess();
                }

                state = HandState.NORMAL;
                selectPoints.clear();
                invalidate();
                setOnTouchListener(null);
            }
        }, delayMillis);
    }

    public void setHandListener(HandViewListener listener) {
        this.listener = listener;
    }

    private void drawRound(Canvas canvas) {
        for (RoundPoint point : points) {
            //绘制外圆
            mRoundPaint.setColor(getOutColorByState(point));
            mRoundPaint.setStrokeWidth(rWidth);
            RectF outF = new RectF(point.pointX - mRadius, point.pointY - mRadius, point.pointX + mRadius, point.pointY + mRadius);
            canvas.drawArc(outF, 0, 360, false, mRoundPaint);
            //绘制内圆
            mRoundPaint.setColor(getInColorByState(point));
            mRoundPaint.setStrokeWidth(rWidth * 2);
            RectF inF = new RectF(point.pointX - mRadius / 4, point.pointY - mRadius / 4, point.pointX + mRadius / 4, point.pointY + mRadius / 4);
            canvas.drawArc(inF, 0, 360, false, mRoundPaint);
        }
    }

    private void drawTrail(Canvas canvas) {
        //设置颜色
        mTrailPaint.setColor(state == HandState.HANDING ? COLOR_BULE_IN : COLOR_RED_IN);

        lock.lock();
        //已经存在的线
        if (selectPoints.size() > 1) {
            RoundPoint lastPoint = null;
            for (Map.Entry<Integer, RoundPoint> entry : selectPoints.entrySet()) {
                RoundPoint point = entry.getValue();
                if (lastPoint != null) {
                    //绘制两点
                    double v = Math.sqrt(Math.abs((point.pointX - lastPoint.pointX) * (point.pointX - lastPoint.pointX) +
                            (point.pointY - lastPoint.pointY) * (point.pointY - lastPoint.pointY)));

                    canvas.drawLine((float) ((point.pointX - lastPoint.pointX) * mRadius / 4 / v) + lastPoint.pointX,
                            (float) ((point.pointY - lastPoint.pointY) * mRadius / 4 / v) + lastPoint.pointY,
                            point.pointX - (float) ((point.pointX - lastPoint.pointX) * mRadius / 4 / v),
                            point.pointY - (float) ((point.pointY - lastPoint.pointY) * mRadius / 4 / v),
                            mTrailPaint);
                }
                lastPoint = point;
            }
        }
        lock.unlock();

        if (isTrailing) {
            //正在绘制的线
            RoundPoint point = points.get(lastPosition);
            double v = Math.sqrt(Math.abs((moveX - point.pointX) * (moveX - point.pointX) +
                    (moveY - point.pointY) * (moveY - point.pointY)));
            canvas.drawLine((float) ((moveX - point.pointX) * mRadius / 4 / v) + point.pointX,
                    (float) ((moveY - point.pointY) * mRadius / 4 / v) + point.pointY, moveX, moveY, mTrailPaint);
        }
    }

    private int isHaveCenterPoint(int lastP, int nowP) {
        if (selectPoints.containsKey(4)) {
            return -1;
        } else {
            for (int i = 0; i < points.size(); i++) {
                RoundPoint centerPoint = points.get(i);

                RoundPoint lastPoint = points.get(lastP);
                RoundPoint nowPoint = points.get(nowP);
                if ((lastPoint.pointX + nowPoint.pointX) / 2 == centerPoint.pointX
                        && (lastPoint.pointY + nowPoint.pointY) / 2 == centerPoint.pointY) {
                    return i;
                }
            }
            return -1;
        }
    }

    private int getOutColorByState(RoundPoint point) {
        int color = 0;
        if (state == HandState.NORMAL) {
            color = COLOR_GRAY_OUT;
        } else if (state == HandState.HANDING) {
            color = selectPoints.containsValue(point) ? COLOR_BULE_OUT : COLOR_GRAY_OUT;
        } else if (state == HandState.ERROR) {
            color = COLOR_RED_OUT;
        }
        return color;
    }

    private int getInColorByState(RoundPoint point) {
        int color = 0;
        if (state == HandState.NORMAL) {
            color = COLOR_GRAY_IN;
        } else if (state == HandState.HANDING) {
            color = selectPoints.containsValue(point) ? COLOR_BULE_IN : COLOR_GRAY_IN;
        } else if (state == HandState.ERROR) {
            color = COLOR_RED_IN;
        }
        return color;
    }

    private int getPositionByXY(float x, float y) {
        for (int i = 0; i < points.size(); i++) {
            RoundPoint point = points.get(i);
            double v = Math.sqrt(Math.abs((x - point.pointX) * (x - point.pointX) +
                    (y - point.pointY) * (y - point.pointY)));
            if (v <= mRadius) {
                return i;
            }
        }
        return -1;
    }
}
